﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using VkcomAPI;
using VkcomAPI.PublicTypes;
using System.Collections.Specialized;
using System.Web;

namespace VkcomAPI.Core
{
    /// <summary>
    /// Main class to interact with vk.com API
    /// </summary>
    public class ApiMain
    {
        private string access_token;
        private string userId;

        /// <summary>
        /// Used to deal with url's
        /// </summary>
        private UrlBuilder UrlBuilder
        {
            get;
            set;
        }

        public string AccesToken
        {
            get
            {
                return access_token;
            }
        }

        public bool Authorize(UserCredentials userCredentials)
        {
            TokenAndID tokenAndId = AuthorizeInternal(userCredentials);
            if (tokenAndId == null)
                return false;

            UrlBuilder = new UrlBuilder(userCredentials, tokenAndId);
            return true;
        }

        private TokenAndID AuthorizeInternal(UserCredentials userCredentials)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(String.Format("http://api.vkontakte.ru/oauth/authorize?client_id={0}&scope={1}&display=wap&response_type=token", VkApiResources.ApplicationID, "audio"));
            HttpWebResponse response = null;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException)
            {
                throw new WebException("Propably you don't have an internet connection.");
            }
            StreamReader responseStream = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            string html = responseStream.ReadToEnd();

            Regex regex = new Regex("<form(.*?)>(?<form_body>.*?)</form>", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);
            if (!regex.IsMatch(html) || (html = regex.Match(html).Groups["form_body"].Value) == "")
                return null;

            regex = new Regex("<input(.*?)name=\"(?<name>[^\x22]+)\"(.*?)((value=\"(?<value>[^\x22]*)\"(.*?))|(.?))>", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);
            NameValueCollection qs = new NameValueCollection();
            foreach (Match match in regex.Matches(html))
            {
                string value = match.Groups["value"].Value;
                if (match.Groups["name"].Value == "email")
                    value = userCredentials.Login;
                else if (match.Groups["name"].Value == "pass")
                    value = userCredentials.Password;
                qs.Add(match.Groups["name"].Value, HttpUtility.UrlEncode(value));
            }

            response = GetResponse(qs);
            CookieContainer cookies = new CookieContainer();

            foreach (Cookie cookie in response.Cookies)
                cookies.Add(cookie);

            if (String.IsNullOrEmpty(response.Headers["Location"]))
                return null;// что-то пошло не так

            // делаем редирект
            request = (HttpWebRequest)HttpWebRequest.Create(response.Headers["Location"]);
            request.CookieContainer = cookies;// передаем куки
            request.Method = "GET";
            request.ContentType = "text/html";

            response = (HttpWebResponse)request.GetResponse();
            responseStream = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            html = responseStream.ReadToEnd();

            regex = new Regex("<form(.*?)action=\"(?<post_url>[^\\x22]+)\"(.*?)>(?<form_body>.*?)</form>", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);
            var TokenAndId = GetTokenAndId(response.ResponseUri.Fragment);
            if (TokenAndId != null)
                return TokenAndId;

            if (!regex.IsMatch(html))
                return null;

            string url = regex.Match(html).Groups["post_url"].Value;

            if (!url.ToLower().StartsWith("http://"))
                url = String.Format("http://api.vkontakte.ru{0}", url);

            request = (HttpWebRequest)HttpWebRequest.Create(url);
            request.CookieContainer = cookies; // не забываем передавать куки
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.AllowAutoRedirect = false;

            response = (HttpWebResponse)request.GetResponse();

            //if (String.IsNullOrEmpty(response.Headers["Location"]))
            //    return null;

            return GetTokenAndId(response);
        }

        private HttpWebResponse GetResponse(NameValueCollection qs)
        {
            byte[] b = System.Text.Encoding.UTF8.GetBytes(String.Join("&", from item in qs.AllKeys
                                                                           select item + "=" + qs[item]));
            var loginRequest = (HttpWebRequest)HttpWebRequest.Create(@"https://login.vk.com/?act=login&soft=1&utf8=1");
            loginRequest.CookieContainer = new CookieContainer();
            loginRequest.Method = "POST";
            loginRequest.ContentType = "application/x-www-form-urlencoded";
            loginRequest.ContentLength = b.Length;
            loginRequest.GetRequestStream().Write(b, 0, b.Length);
            loginRequest.AllowAutoRedirect = false;

            return (HttpWebResponse)loginRequest.GetResponse();
        }

        private TokenAndID GetTokenAndId(string fragment)
        {
            string[] tokens = fragment.Split('&');
            if (tokens.Count() != 3)
                return null;

            // trying to parse access token and id from string 
            string access_token = tokens[0].Split('=')[1];
            string user_id = tokens[2].Split('=')[1];
            return new TokenAndID(access_token, user_id);
        }

        private TokenAndID GetTokenAndId(HttpWebResponse myResp)
        {
            Regex regex = new Regex(@"(?<name>[\w\d\x5f]+)=(?<value>[^\x26\s]+)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            foreach (Match m in regex.Matches(myResp.Headers["Location"]))
            {
                string value = m.Groups["name"].Value;
                switch (value)
                {
                    case "access_token":
                        access_token = value;
                        break;
                    case "user_id":
                        userId = value;
                        break;
                }
            }

            if (!string.IsNullOrEmpty(access_token) && string.IsNullOrEmpty(userId))
                return new TokenAndID(access_token, userId);
            else
                return null;
        }

        public virtual List<SongInfo> SearchAudio(string searchString)
        {
            string requestString = UrlBuilder.CreateSearchUri(searchString);
            WebRequest request = HttpWebRequest.Create(requestString);
            WebResponse response = request.GetResponse();

            List<SongInfo> songsList = new List<SongInfo>();
            Stream responseStream = response.GetResponseStream();
            var results = XElement.Load(responseStream).Elements("audio");
            for (int i = 0; i < results.Count() && i < 30; i++)
                songsList.Add(new SongInfo(results.ElementAt(i)));
            return songsList;
        }

        public XElement GetAllMusic()
        {
            string requestString = UrlBuilder.CreateGetAllMusicUri();
            WebRequest request = HttpWebRequest.Create(requestString);
            IAsyncResult asyncres = request.BeginGetResponse(null, null);
            WebResponse resp = request.EndGetResponse(asyncres);

            return XElement.Load(resp.GetResponseStream());
        }
    }
}
